From 1e2fcd2a0d387a0d10c46547f50fbb1092cfc696 Mon Sep 17 00:00:00 2001 From: Iceyer Date: Tue, 25 Jul 2017 20:27:53 +0800 Subject: [PATCH] Merge filesystem --- src/base/{dbase.pri => base.pri} | 6 + src/base/dbase.pro | 20 - src/filesystem/DBaseFileWatcher | 1 + src/filesystem/DFileSystemWatcher | 1 + src/filesystem/DFileWatcher | 1 + src/filesystem/DFileWatcherManager | 1 + src/filesystem/DPathBuf | 1 + src/filesystem/dbasefilewatcher.cpp | 129 ++++ src/filesystem/dbasefilewatcher.h | 59 ++ src/filesystem/dfilesystemwatcher.h | 54 ++ src/filesystem/dfilesystemwatcher_linux.cpp | 638 ++++++++++++++++++ src/filesystem/dfilesystemwatcher_win.cpp | 240 +++++++ src/filesystem/dfilewatcher.cpp | 277 ++++++++ src/filesystem/dfilewatcher.h | 40 ++ src/filesystem/dfilewatchermanager.cpp | 95 +++ src/filesystem/dfilewatchermanager.h | 51 ++ src/filesystem/dpathbuf.h | 53 ++ src/filesystem/filesystem.pri | 37 + src/filesystem/private/dbasefilewatcher_p.h | 37 + .../private/dfilesystemwatcher_linux_p.h | 50 ++ .../private/dfilesystemwatcher_win_p.h | 36 + src/filesystem/private/private.pri | 10 + src/install.pri | 11 - src/log/log.pri | 5 + src/src.pro | 8 +- 25 files changed, 1828 insertions(+), 33 deletions(-) rename src/base/{dbase.pri => base.pri} (52%) delete mode 100644 src/base/dbase.pro create mode 100644 src/filesystem/DBaseFileWatcher create mode 100644 src/filesystem/DFileSystemWatcher create mode 100644 src/filesystem/DFileWatcher create mode 100644 src/filesystem/DFileWatcherManager create mode 100644 src/filesystem/DPathBuf create mode 100644 src/filesystem/dbasefilewatcher.cpp create mode 100644 src/filesystem/dbasefilewatcher.h create mode 100644 src/filesystem/dfilesystemwatcher.h create mode 100644 src/filesystem/dfilesystemwatcher_linux.cpp create mode 100644 src/filesystem/dfilesystemwatcher_win.cpp create mode 100644 src/filesystem/dfilewatcher.cpp create mode 100644 src/filesystem/dfilewatcher.h create mode 100644 src/filesystem/dfilewatchermanager.cpp create mode 100644 src/filesystem/dfilewatchermanager.h create mode 100644 src/filesystem/dpathbuf.h create mode 100644 src/filesystem/filesystem.pri create mode 100644 src/filesystem/private/dbasefilewatcher_p.h create mode 100644 src/filesystem/private/dfilesystemwatcher_linux_p.h create mode 100644 src/filesystem/private/dfilesystemwatcher_win_p.h create mode 100644 src/filesystem/private/private.pri diff --git a/src/base/dbase.pri b/src/base/base.pri similarity index 52% rename from src/base/dbase.pri rename to src/base/base.pri index 5dc9121..a061868 100644 --- a/src/base/dbase.pri +++ b/src/base/base.pri @@ -6,3 +6,9 @@ HEADERS += \ SOURCES += \ $$PWD/dobject.cpp + +includes.files += $$PWD/*.h +includes.files += $$PWD/*.cpp +includes.files += \ + $$PWD/DObject \ + $$PWD/DSingleton diff --git a/src/base/dbase.pro b/src/base/dbase.pro deleted file mode 100644 index bb7f80c..0000000 --- a/src/base/dbase.pro +++ /dev/null @@ -1,20 +0,0 @@ -DCOMMON_DIR = $$PWD/../common -include($$DCOMMON_DIR/lib.pri) -include($$PWD/dbase.pri) - -QT -= gui - -TARGET = dtkbase - -DEFINES += LIBDTKBASE_LIBRARY - -includes.path = $${DTK_INCLUDEPATH}/DBase -includes.files += *.h -includes.files += DObject - -QMAKE_PKGCONFIG_NAME = DTK_BASE -QMAKE_PKGCONFIG_DESCRIPTION = Deepin Tool Kit Base Header Files -QMAKE_PKGCONFIG_INCDIR = $$includes.path - -DISTFILES += \ - DSingleton diff --git a/src/filesystem/DBaseFileWatcher b/src/filesystem/DBaseFileWatcher new file mode 100644 index 0000000..1348031 --- /dev/null +++ b/src/filesystem/DBaseFileWatcher @@ -0,0 +1 @@ +#include "dbasefilewatcher.h" diff --git a/src/filesystem/DFileSystemWatcher b/src/filesystem/DFileSystemWatcher new file mode 100644 index 0000000..97400f2 --- /dev/null +++ b/src/filesystem/DFileSystemWatcher @@ -0,0 +1 @@ +#include "dfilesystemwatcher.h" diff --git a/src/filesystem/DFileWatcher b/src/filesystem/DFileWatcher new file mode 100644 index 0000000..768151a --- /dev/null +++ b/src/filesystem/DFileWatcher @@ -0,0 +1 @@ +#include "dfilewatcher.h" diff --git a/src/filesystem/DFileWatcherManager b/src/filesystem/DFileWatcherManager new file mode 100644 index 0000000..a9f8682 --- /dev/null +++ b/src/filesystem/DFileWatcherManager @@ -0,0 +1 @@ +#include "dfilewatchermanager.h" diff --git a/src/filesystem/DPathBuf b/src/filesystem/DPathBuf new file mode 100644 index 0000000..961c40b --- /dev/null +++ b/src/filesystem/DPathBuf @@ -0,0 +1 @@ +#include "dpathbuf.h" diff --git a/src/filesystem/dbasefilewatcher.cpp b/src/filesystem/dbasefilewatcher.cpp new file mode 100644 index 0000000..c2ca79f --- /dev/null +++ b/src/filesystem/dbasefilewatcher.cpp @@ -0,0 +1,129 @@ +/** + * Copyright (C) 2017 Deepin Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + **/ + +#include "dbasefilewatcher.h" +#include "private/dbasefilewatcher_p.h" + +#include +#include + +DCORE_BEGIN_NAMESPACE + +QList DBaseFileWatcherPrivate::watcherList; +DBaseFileWatcherPrivate::DBaseFileWatcherPrivate(DBaseFileWatcher *qq) + : DObjectPrivate(qq) +{ + +} + +DBaseFileWatcher::~DBaseFileWatcher() +{ + stopWatcher(); + DBaseFileWatcherPrivate::watcherList.removeOne(this); +} + +QUrl DBaseFileWatcher::fileUrl() const +{ + Q_D(const DBaseFileWatcher); + + return d->url; +} + +bool DBaseFileWatcher::startWatcher() +{ + Q_D(DBaseFileWatcher); + + if (d->started) + return true; + + if (d->start()) { + d->started = true; + + return true; + } + + return false; +} + +bool DBaseFileWatcher::stopWatcher() +{ + Q_D(DBaseFileWatcher); + + if (!d->started) + return false; + + if (d->stop()) { + d->started = false; + + return true; + } + + return false; +} + +bool DBaseFileWatcher::restartWatcher() +{ + bool ok = stopWatcher(); + return ok && startWatcher(); +} + +void DBaseFileWatcher::setEnabledSubfileWatcher(const QUrl &subfileUrl, bool enabled) +{ + Q_UNUSED(subfileUrl) + Q_UNUSED(enabled) +} + +bool DBaseFileWatcher::ghostSignal(const QUrl &targetUrl, DBaseFileWatcher::SignalType1 signal, const QUrl &arg1) +{ + if (!signal) + return false; + + bool ok = false; + + for (DBaseFileWatcher *watcher : DBaseFileWatcherPrivate::watcherList) { + if (watcher->fileUrl() == targetUrl) { + ok = true; + (watcher->*signal)(arg1); + } + } + + return ok; +} + +bool DBaseFileWatcher::ghostSignal(const QUrl &targetUrl, DBaseFileWatcher::SignalType2 signal, const QUrl &arg1, const QUrl &arg2) +{ + if (!signal) + return false; + + bool ok = false; + + for (DBaseFileWatcher *watcher : DBaseFileWatcherPrivate::watcherList) { + if (watcher->fileUrl() == targetUrl) { + ok = true; + (watcher->*signal)(arg1, arg2); + } + } + + return ok; +} + +DBaseFileWatcher::DBaseFileWatcher(DBaseFileWatcherPrivate &dd, + const QUrl &url, QObject *parent) + : QObject(parent) + , DObject(dd) +{ + Q_ASSERT(url.isValid()); + + d_func()->url = url; + DBaseFileWatcherPrivate::watcherList << this; +} + +DCORE_END_NAMESPACE + +#include "moc_dbasefilewatcher.cpp" diff --git a/src/filesystem/dbasefilewatcher.h b/src/filesystem/dbasefilewatcher.h new file mode 100644 index 0000000..2d0b37b --- /dev/null +++ b/src/filesystem/dbasefilewatcher.h @@ -0,0 +1,59 @@ +/** + * Copyright (C) 2017 Deepin Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + **/ + +#ifndef DBASEFILEWATCHER_H +#define DBASEFILEWATCHER_H + +#include "dtkcore_global.h" +#include "base/dobject.h" + +#include + +DCORE_BEGIN_NAMESPACE + +class DBaseFileWatcherPrivate; +class DBaseFileWatcher : public QObject, public DObject +{ + Q_OBJECT + +public: + ~DBaseFileWatcher(); + + QUrl fileUrl() const; + + bool startWatcher(); + bool stopWatcher(); + bool restartWatcher(); + + virtual void setEnabledSubfileWatcher(const QUrl &subfileUrl, bool enabled = true); + + using SignalType1 = void(DBaseFileWatcher::*)(const QUrl &); + using SignalType2 = void(DBaseFileWatcher::*)(const QUrl &, const QUrl &); + static bool ghostSignal(const QUrl &targetUrl, SignalType1 signal, const QUrl &arg1); + static bool ghostSignal(const QUrl &targetUrl, SignalType2 signal, const QUrl &arg1, const QUrl &arg2); + +Q_SIGNALS: + void fileDeleted(const QUrl &url); + void fileAttributeChanged(const QUrl &url); + void fileMoved(const QUrl &fromUrl, const QUrl &toUrl); + void subfileCreated(const QUrl &url); + void fileModified(const QUrl &url); + void fileClosed(const QUrl &url); + +protected: + explicit DBaseFileWatcher(DBaseFileWatcherPrivate &dd, const QUrl &url, QObject *parent = 0); + +private: + Q_DISABLE_COPY(DBaseFileWatcher) + D_DECLARE_PRIVATE(DBaseFileWatcher) +}; + +DCORE_END_NAMESPACE + +#endif // DBASEFILEWATCHER_H diff --git a/src/filesystem/dfilesystemwatcher.h b/src/filesystem/dfilesystemwatcher.h new file mode 100644 index 0000000..39cdda5 --- /dev/null +++ b/src/filesystem/dfilesystemwatcher.h @@ -0,0 +1,54 @@ +/** + * Copyright (C) 2017 Deepin Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + **/ + +#ifndef DFILESYSTEMWATCHER_H +#define DFILESYSTEMWATCHER_H + +#include "dtkcore_global.h" +#include "base/dobject.h" + +#include + +DCORE_BEGIN_NAMESPACE + +class DFileSystemWatcherPrivate; +class DFileSystemWatcher : public QObject, public DObject +{ + Q_OBJECT + D_DECLARE_PRIVATE(DFileSystemWatcher) + +public: + DFileSystemWatcher(QObject *parent = Q_NULLPTR); + DFileSystemWatcher(const QStringList &paths, QObject *parent = Q_NULLPTR); + ~DFileSystemWatcher(); + + bool addPath(const QString &file); + QStringList addPaths(const QStringList &files); + bool removePath(const QString &file); + QStringList removePaths(const QStringList &files); + + QStringList files() const; + QStringList directories() const; + +Q_SIGNALS: + void fileDeleted(const QString &path, const QString &name, QPrivateSignal); + void fileAttributeChanged(const QString &path, const QString &name, QPrivateSignal); + void fileClosed(const QString &path, const QString &name, QPrivateSignal); + void fileMoved(const QString &fromPath, const QString &fromName, + const QString &toPath, const QString &toName, QPrivateSignal); + void fileCreated(const QString &path, const QString &name, QPrivateSignal); + void fileModified(const QString &path, const QString &name, QPrivateSignal); + +private: + Q_PRIVATE_SLOT(d_func(), void _q_readFromInotify()) +}; + +DCORE_END_NAMESPACE + +#endif // DFILESYSTEMWATCHER_H diff --git a/src/filesystem/dfilesystemwatcher_linux.cpp b/src/filesystem/dfilesystemwatcher_linux.cpp new file mode 100644 index 0000000..aa1f637 --- /dev/null +++ b/src/filesystem/dfilesystemwatcher_linux.cpp @@ -0,0 +1,638 @@ +/** + * Copyright (C) 2017 Deepin Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + **/ + +#include "dfilesystemwatcher.h" +#include "private/dfilesystemwatcher_linux_p.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +DFileSystemWatcherPrivate::DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq) + : DObjectPrivate(qq) + , inotifyFd(fd) + , notifier(fd, QSocketNotifier::Read, qq) +{ + fcntl(inotifyFd, F_SETFD, FD_CLOEXEC); + qq->connect(¬ifier, SIGNAL(activated(int)), qq, SLOT(_q_readFromInotify())); +} + +DFileSystemWatcherPrivate::~DFileSystemWatcherPrivate() +{ + notifier.setEnabled(false); + Q_FOREACH (int id, pathToID) + inotify_rm_watch(inotifyFd, id < 0 ? -id : id); + + ::close(inotifyFd); +} + +QStringList DFileSystemWatcherPrivate::addPaths(const QStringList &paths, QStringList *files, QStringList *directories) +{ + QStringList p = paths; + QMutableListIterator it(p); + while (it.hasNext()) { + QString path = it.next(); + QFileInfo fi(path); + bool isDir = fi.isDir(); + if (isDir) { + if (directories->contains(path)) + continue; + } else { + if (files->contains(path)) + continue; + } + + int wd = inotify_add_watch(inotifyFd, + QFile::encodeName(path), + (isDir + ? (0 + | IN_ATTRIB + | IN_MOVE + | IN_MOVE_SELF + | IN_CREATE + | IN_DELETE + | IN_DELETE_SELF + | IN_MODIFY + ) + : (0 + | IN_ATTRIB + | IN_CLOSE_WRITE + | IN_MODIFY + | IN_MOVE + | IN_MOVE_SELF + | IN_DELETE_SELF + ))); + if (wd < 0) { + perror("DFileSystemWatcherPrivate::addPaths: inotify_add_watch failed"); + continue; + } + + it.remove(); + + int id = isDir ? -wd : wd; + if (id < 0) { + directories->append(path); + } else { + files->append(path); + } + + pathToID.insert(path, id); + idToPath.insert(id, path); + } + + return p; +} + +QStringList DFileSystemWatcherPrivate::removePaths(const QStringList &paths, QStringList *files, QStringList *directories) +{ + QStringList p = paths; + QMutableListIterator it(p); + while (it.hasNext()) { + QString path = it.next(); + int id = pathToID.take(path); + QString x = idToPath.take(id); + + it.remove(); + + if (x.isEmpty() || x != path) + continue; + + int wd = id < 0 ? -id : id; + // qDebug() << "removing watch for path" << path << "wd" << wd; + inotify_rm_watch(inotifyFd, wd); + + if (id < 0) { + directories->removeAll(path); + } else { + files->removeAll(path); + } + } + + return p; +} + +void DFileSystemWatcherPrivate::_q_readFromInotify() +{ + Q_Q(DFileSystemWatcher); +// qDebug() << "QInotifyFileSystemWatcherEngine::readFromInotify"; + + int buffSize = 0; + ioctl(inotifyFd, FIONREAD, (char *) &buffSize); + QVarLengthArray buffer(buffSize); + buffSize = read(inotifyFd, buffer.data(), buffSize); + char *at = buffer.data(); + char * const end = at + buffSize; + + QList eventList; + QHash pathForId; + /// only save event: IN_MOVE_TO + QMap cookieToFilePath; + QMap cookieToFileName; + QSet hasMoveFromByCookie; + while (at < end) { + inotify_event *event = reinterpret_cast(at); + QString path; + + at += sizeof(inotify_event) + event->len; + + int id = event->wd; + path = getPathFromID(id); + if (path.isEmpty()) { + // perhaps a directory? + id = -id; + path = getPathFromID(id); + if (path.isEmpty()) + continue; + } + + if (!(event->mask & IN_MOVED_TO) || !hasMoveFromByCookie.contains(event->cookie)) { + eventList.append(event); + pathForId.insert(id, path); + } + + if (event->mask & IN_MOVED_TO) { + cookieToFilePath.insert(event->cookie, path); + cookieToFileName.insert(event->cookie, QString::fromUtf8(event->name)); + } + + if (event->mask & IN_MOVED_FROM) + hasMoveFromByCookie << event->cookie; + } + +// qDebug() << "event count:" << eventList.count(); + + QList::const_iterator it = eventList.constBegin(); + while (it != eventList.constEnd()) { + const inotify_event &event = **it; + ++it; + +// qDebug() << "inotify event, wd" << event.wd << "cookie" << event.cookie << "mask" << hex << event.mask; + + int id = event.wd; + QString path = pathForId.value(id); + + if (path.isEmpty()) { + id = -id; + path = pathForId.value(id); + + if (path.isEmpty()) + continue; + } + const QString &name = QString::fromUtf8(event.name); + +// qDebug() << "event for path" << path; + +// /// TODO: Existence of invalid utf8 characters QFile can not read the file information +// if (event.name != QString::fromLocal8Bit(event.name).toLocal8Bit()) { +// if (event.mask & (IN_CREATE | IN_MOVED_TO)) { +// DFMGlobal::fileNameCorrection(path); +// } +// } + + if ((event.mask & (IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT)) != 0) { + do { + if (event.mask & IN_MOVE_SELF) { + QMap::const_iterator iterator = cookieToFilePath.constBegin(); + + bool isMove = false; + + while (iterator != cookieToFilePath.constEnd()) { + const QString &_path = iterator.value(); + const QString &_name = cookieToFileName.value(iterator.key()); + + if (QFileInfo(_path + QDir::separator() + _name) == QFileInfo(path)) { + isMove = true; + break; + } + + ++iterator; + } + + if (isMove) + break; + } + + /// Keep watcher +// pathToID.remove(path); +// idToPath.remove(id, getPathFromID(id)); +// if (!idToPath.contains(id)) +// inotify_rm_watch(inotifyFd, event.wd); + +// if (id < 0) +// onDirectoryChanged(path, true); +// else +// onFileChanged(path, true); + + Q_EMIT q->fileDeleted(path, QString(), DFileSystemWatcher::QPrivateSignal()); + } while (false); + } else { + if (id < 0) + onDirectoryChanged(path, false); + else + onFileChanged(path, false); + } + + QString filePath = path; + + if (id < 0) { + if (path.endsWith(QDir::separator())) + filePath = path + name; + else + filePath = path + QDir::separator() + name; + } + + if (event.mask & IN_CREATE) { +// qDebug() << "IN_CREATE" << filePath << name; + + if (name.isEmpty()) { + if (pathToID.contains(path)) { + q->removePath(path); + q->addPath(path); + } + } else if (pathToID.contains(filePath)) { + q->removePath(filePath); + q->addPath(filePath); + } + + Q_EMIT q->fileCreated(path, name, DFileSystemWatcher::QPrivateSignal()); + } + + if (event.mask & IN_DELETE) { +// qDebug() << "IN_DELETE" << filePath; + + Q_EMIT q->fileDeleted(path, name, DFileSystemWatcher::QPrivateSignal()); + } + + if (event.mask & IN_MOVED_FROM) { + const QString &toPath = cookieToFilePath.value(event.cookie); + const QString toName = cookieToFileName.value(event.cookie); + +// qDebug() << "IN_MOVED_FROM" << filePath << "to path:" << toPath << "to name:" << toName; + + Q_EMIT q->fileMoved(path, name, toPath, toName, DFileSystemWatcher::QPrivateSignal()); + } + + if (event.mask & IN_MOVED_TO) { +// qDebug() << "IN_MOVED_TO" << filePath; + + if (!hasMoveFromByCookie.contains(event.cookie)) + Q_EMIT q->fileMoved(QString(), QString(), path, name, DFileSystemWatcher::QPrivateSignal()); + } + + if (event.mask & IN_ATTRIB) { +// qDebug() << "IN_ATTRIB" << event.mask << filePath; + + Q_EMIT q->fileAttributeChanged(path, name, DFileSystemWatcher::QPrivateSignal()); + } + + /*only monitor file close event which is opend by write mode*/ + if (event.mask & IN_CLOSE_WRITE) { +// qDebug() << "IN_CLOSE_WRITE" << event.mask << filePath; + + Q_EMIT q->fileClosed(path, id < 0 ? name : QString(), DFileSystemWatcher::QPrivateSignal()); + } + + if (event.mask & IN_MODIFY) { +// qDebug() << "IN_MODIFY" << event.mask << filePath << name; + + Q_EMIT q->fileModified(path, name, DFileSystemWatcher::QPrivateSignal()); + } + } +} + +QString DFileSystemWatcherPrivate::getPathFromID(int id) const +{ + QHash::const_iterator i = idToPath.find(id); + while (i != idToPath.constEnd() && i.key() == id) { + if ((i + 1) == idToPath.constEnd() || (i + 1).key() != id) { + return i.value(); + } + ++i; + } + return QString(); +} + +void DFileSystemWatcherPrivate::onFileChanged(const QString &path, bool removed) +{ + Q_Q(DFileSystemWatcher); + if (!files.contains(path)) { + // the path was removed after a change was detected, but before we delivered the signal + return; + } + if (removed) { + files.removeAll(path); + } +// Q_EMIT q->fileChanged(path, DFileSystemWatcher::QPrivateSignal()); +} + +void DFileSystemWatcherPrivate::onDirectoryChanged(const QString &path, bool removed) +{ + Q_Q(DFileSystemWatcher); + if (!directories.contains(path)) { + // perhaps the path was removed after a change was detected, but before we delivered the signal + return; + } + if (removed) { + directories.removeAll(path); + } +// Q_EMIT q->directoryChanged(path, DFileSystemWatcher::QPrivateSignal()); +} + +/*! + \class DFileSystemWatcher + \inmodule QtCore + \brief The DFileSystemWatcher class provides an interface for monitoring files and directories for modifications. + \ingroup io + \since 4.2 + \reentrant + + DFileSystemWatcher monitors the file system for changes to files + and directories by watching a list of specified paths. + + Call addPath() to watch a particular file or directory. Multiple + paths can be added using the addPaths() function. Existing paths can + be removed by using the removePath() and removePaths() functions. + + DFileSystemWatcher examines each path added to it. Files that have + been added to the DFileSystemWatcher can be accessed using the + files() function, and directories using the directories() function. + + The fileChanged() signal is emitted when a file has been modified, + renamed or removed from disk. Similarly, the directoryChanged() + signal is emitted when a directory or its contents is modified or + removed. Note that DFileSystemWatcher stops monitoring files once + they have been renamed or removed from disk, and directories once + they have been removed from disk. + + \note On systems running a Linux kernel without inotify support, + file systems that contain watched paths cannot be unmounted. + + \note Windows CE does not support directory monitoring by + default as this depends on the file system driver installed. + + \note The act of monitoring files and directories for + modifications consumes system resources. This implies there is a + limit to the number of files and directories your process can + monitor simultaneously. On all BSD variants, for + example, an open file descriptor is required for each monitored + file. Some system limits the number of open file descriptors to 256 + by default. This means that addPath() and addPaths() will fail if + your process tries to add more than 256 files or directories to + the file system monitor. Also note that your process may have + other file descriptors open in addition to the ones for files + being monitored, and these other open descriptors also count in + the total. OS X uses a different backend and does not + suffer from this issue. + + + \sa QFile, QDir +*/ + + +/*! + Constructs a new file system watcher object with the given \a parent. +*/ +DFileSystemWatcher::DFileSystemWatcher(QObject *parent) + : QObject(parent) + , DObject() +{ + int fd = -1; +#ifdef IN_CLOEXEC + fd = inotify_init1(IN_CLOEXEC); +#endif + if (fd == -1) { + fd = inotify_init(); + } + + if (fd != -1) + d_d_ptr.reset(new DFileSystemWatcherPrivate(fd, this)); +} + +/*! + Constructs a new file system watcher object with the given \a parent + which monitors the specified \a paths list. +*/ +DFileSystemWatcher::DFileSystemWatcher(const QStringList &paths, QObject *parent) + : DFileSystemWatcher(parent) +{ + addPaths(paths); +} + +/*! + Destroys the file system watcher. +*/ +DFileSystemWatcher::~DFileSystemWatcher() +{ } + +/*! + Adds \a path to the file system watcher if \a path exists. The + path is not added if it does not exist, or if it is already being + monitored by the file system watcher. + + If \a path specifies a directory, the directoryChanged() signal + will be emitted when \a path is modified or removed from disk; + otherwise the fileChanged() signal is emitted when \a path is + modified, renamed or removed. + + If the watch was successful, true is returned. + + Reasons for a watch failure are generally system-dependent, but + may include the resource not existing, access failures, or the + total watch count limit, if the platform has one. + + \note There may be a system dependent limit to the number of + files and directories that can be monitored simultaneously. + If this limit is been reached, \a path will not be monitored, + and false is returned. + + \sa addPaths(), removePath() +*/ +bool DFileSystemWatcher::addPath(const QString &path) +{ + if (path.isEmpty()) { + qWarning("DFileSystemWatcher::addPath: path is empty"); + return true; + } + + QStringList paths = addPaths(QStringList(path)); + return paths.isEmpty(); +} + +/*! + Adds each path in \a paths to the file system watcher. Paths are + not added if they not exist, or if they are already being + monitored by the file system watcher. + + If a path specifies a directory, the directoryChanged() signal + will be emitted when the path is modified or removed from disk; + otherwise the fileChanged() signal is emitted when the path is + modified, renamed, or removed. + + The return value is a list of paths that could not be watched. + + Reasons for a watch failure are generally system-dependent, but + may include the resource not existing, access failures, or the + total watch count limit, if the platform has one. + + \note There may be a system dependent limit to the number of + files and directories that can be monitored simultaneously. + If this limit has been reached, the excess \a paths will not + be monitored, and they will be added to the returned QStringList. + + \sa addPath(), removePaths() +*/ +QStringList DFileSystemWatcher::addPaths(const QStringList &paths) +{ + Q_D(DFileSystemWatcher); + + QStringList p = paths; + QMutableListIterator it(p); + + while (it.hasNext()) { + const QString &path = it.next(); + if (path.isEmpty()) + it.remove(); + } + + if (p.isEmpty()) { + qWarning("DFileSystemWatcher::addPaths: list is empty"); + return QStringList(); + } + + if (d) + p = d->addPaths(p, &d->files, &d->directories); + + return p; +} + +/*! + Removes the specified \a path from the file system watcher. + + If the watch is successfully removed, true is returned. + + Reasons for watch removal failing are generally system-dependent, + but may be due to the path having already been deleted, for example. + + \sa removePaths(), addPath() +*/ +bool DFileSystemWatcher::removePath(const QString &path) +{ + if (path.isEmpty()) { + qWarning("DFileSystemWatcher::removePath: path is empty"); + return true; + } + + QStringList paths = removePaths(QStringList(path)); + return paths.isEmpty(); +} + +/*! + Removes the specified \a paths from the file system watcher. + + The return value is a list of paths which were not able to be + unwatched successfully. + + Reasons for watch removal failing are generally system-dependent, + but may be due to the path having already been deleted, for example. + + \sa removePath(), addPaths() +*/ +QStringList DFileSystemWatcher::removePaths(const QStringList &paths) +{ + Q_D(DFileSystemWatcher); + + QStringList p = paths; + QMutableListIterator it(p); + + while (it.hasNext()) { + const QString &path = it.next(); + if (path.isEmpty()) + it.remove(); + } + + if (p.isEmpty()) { + qWarning("DFileSystemWatcher::removePaths: list is empty"); + return QStringList(); + } + + if (d) + p = d->removePaths(p, &d->files, &d->directories); + + return p; +} + +/*! + \fn void DFileSystemWatcher::fileChanged(const QString &path) + + This signal is emitted when the file at the specified \a path is + modified, renamed or removed from disk. + + \sa directoryChanged() +*/ + +/*! + \fn void DFileSystemWatcher::directoryChanged(const QString &path) + + This signal is emitted when the directory at a specified \a path + is modified (e.g., when a file is added or deleted) or removed + from disk. Note that if there are several changes during a short + period of time, some of the changes might not Q_EMIT this signal. + However, the last change in the sequence of changes will always + generate this signal. + + \sa fileChanged() +*/ + +/*! + \fn QStringList DFileSystemWatcher::directories() const + + Returns a list of paths to directories that are being watched. + + \sa files() +*/ + +/*! + \fn QStringList DFileSystemWatcher::files() const + + Returns a list of paths to files that are being watched. + + \sa directories() +*/ + +QStringList DFileSystemWatcher::directories() const +{ + Q_D(const DFileSystemWatcher); + + if (!d) + return QStringList(); + + return d->directories; +} + +QStringList DFileSystemWatcher::files() const +{ + Q_D(const DFileSystemWatcher); + + if (!d) + return QStringList(); + + return d->files; +} + +DCORE_END_NAMESPACE + +#include "moc_dfilesystemwatcher.cpp" diff --git a/src/filesystem/dfilesystemwatcher_win.cpp b/src/filesystem/dfilesystemwatcher_win.cpp new file mode 100644 index 0000000..69bd597 --- /dev/null +++ b/src/filesystem/dfilesystemwatcher_win.cpp @@ -0,0 +1,240 @@ +/** + * Copyright (C) 2017 Deepin Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + **/ + +#include "dfilesystemwatcher.h" +#include "private/dfilesystemwatcher_win_p.h" + +DCORE_BEGIN_NAMESPACE + +DFileSystemWatcherPrivate::DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq) + : DObjectPrivate(qq) +{ + +} + +DFileSystemWatcherPrivate::~DFileSystemWatcherPrivate() +{ + +} + +/*! + \class DFileSystemWatcher + \inmodule QtCore + \brief The DFileSystemWatcher class provides an interface for monitoring files and directories for modifications. + \ingroup io + \since 4.2 + \reentrant + + DFileSystemWatcher monitors the file system for changes to files + and directories by watching a list of specified paths. + + Call addPath() to watch a particular file or directory. Multiple + paths can be added using the addPaths() function. Existing paths can + be removed by using the removePath() and removePaths() functions. + + DFileSystemWatcher examines each path added to it. Files that have + been added to the DFileSystemWatcher can be accessed using the + files() function, and directories using the directories() function. + + The fileChanged() signal is emitted when a file has been modified, + renamed or removed from disk. Similarly, the directoryChanged() + signal is emitted when a directory or its contents is modified or + removed. Note that DFileSystemWatcher stops monitoring files once + they have been renamed or removed from disk, and directories once + they have been removed from disk. + + \note On systems running a Linux kernel without inotify support, + file systems that contain watched paths cannot be unmounted. + + \note Windows CE does not support directory monitoring by + default as this depends on the file system driver installed. + + \note The act of monitoring files and directories for + modifications consumes system resources. This implies there is a + limit to the number of files and directories your process can + monitor simultaneously. On all BSD variants, for + example, an open file descriptor is required for each monitored + file. Some system limits the number of open file descriptors to 256 + by default. This means that addPath() and addPaths() will fail if + your process tries to add more than 256 files or directories to + the file system monitor. Also note that your process may have + other file descriptors open in addition to the ones for files + being monitored, and these other open descriptors also count in + the total. OS X uses a different backend and does not + suffer from this issue. + + + \sa QFile, QDir +*/ + + +/*! + Constructs a new file system watcher object with the given \a parent. +*/ +DFileSystemWatcher::DFileSystemWatcher(QObject *parent) + : QObject(parent) + , DObject() +{ + +} + +/*! + Constructs a new file system watcher object with the given \a parent + which monitors the specified \a paths list. +*/ +DFileSystemWatcher::DFileSystemWatcher(const QStringList &paths, QObject *parent) + : DFileSystemWatcher(parent) +{ + addPaths(paths); +} + +/*! + Destroys the file system watcher. +*/ +DFileSystemWatcher::~DFileSystemWatcher() +{ } + +/*! + Adds \a path to the file system watcher if \a path exists. The + path is not added if it does not exist, or if it is already being + monitored by the file system watcher. + + If \a path specifies a directory, the directoryChanged() signal + will be emitted when \a path is modified or removed from disk; + otherwise the fileChanged() signal is emitted when \a path is + modified, renamed or removed. + + If the watch was successful, true is returned. + + Reasons for a watch failure are generally system-dependent, but + may include the resource not existing, access failures, or the + total watch count limit, if the platform has one. + + \note There may be a system dependent limit to the number of + files and directories that can be monitored simultaneously. + If this limit is been reached, \a path will not be monitored, + and false is returned. + + \sa addPaths(), removePath() +*/ +bool DFileSystemWatcher::addPath(const QString &path) +{ + return false; +} + +/*! + Adds each path in \a paths to the file system watcher. Paths are + not added if they not exist, or if they are already being + monitored by the file system watcher. + + If a path specifies a directory, the directoryChanged() signal + will be emitted when the path is modified or removed from disk; + otherwise the fileChanged() signal is emitted when the path is + modified, renamed, or removed. + + The return value is a list of paths that could not be watched. + + Reasons for a watch failure are generally system-dependent, but + may include the resource not existing, access failures, or the + total watch count limit, if the platform has one. + + \note There may be a system dependent limit to the number of + files and directories that can be monitored simultaneously. + If this limit has been reached, the excess \a paths will not + be monitored, and they will be added to the returned QStringList. + + \sa addPath(), removePaths() +*/ +QStringList DFileSystemWatcher::addPaths(const QStringList &paths) +{ + return QStringList(); +} + +/*! + Removes the specified \a path from the file system watcher. + + If the watch is successfully removed, true is returned. + + Reasons for watch removal failing are generally system-dependent, + but may be due to the path having already been deleted, for example. + + \sa removePaths(), addPath() +*/ +bool DFileSystemWatcher::removePath(const QString &path) +{ + return false; +} + +/*! + Removes the specified \a paths from the file system watcher. + + The return value is a list of paths which were not able to be + unwatched successfully. + + Reasons for watch removal failing are generally system-dependent, + but may be due to the path having already been deleted, for example. + + \sa removePath(), addPaths() +*/ +QStringList DFileSystemWatcher::removePaths(const QStringList &paths) +{ + return QStringList(); +} + +/*! + \fn void DFileSystemWatcher::fileChanged(const QString &path) + + This signal is emitted when the file at the specified \a path is + modified, renamed or removed from disk. + + \sa directoryChanged() +*/ + +/*! + \fn void DFileSystemWatcher::directoryChanged(const QString &path) + + This signal is emitted when the directory at a specified \a path + is modified (e.g., when a file is added or deleted) or removed + from disk. Note that if there are several changes during a short + period of time, some of the changes might not Q_EMIT this signal. + However, the last change in the sequence of changes will always + generate this signal. + + \sa fileChanged() +*/ + +/*! + \fn QStringList DFileSystemWatcher::directories() const + + Returns a list of paths to directories that are being watched. + + \sa files() +*/ + +/*! + \fn QStringList DFileSystemWatcher::files() const + + Returns a list of paths to files that are being watched. + + \sa directories() +*/ + +QStringList DFileSystemWatcher::directories() const +{ + return QStringList(); +} + +QStringList DFileSystemWatcher::files() const +{ + return QStringList(); +} + +DCORE_END_NAMESPACE + +#include "moc_dfilesystemwatcher.cpp" diff --git a/src/filesystem/dfilewatcher.cpp b/src/filesystem/dfilewatcher.cpp new file mode 100644 index 0000000..1e47872 --- /dev/null +++ b/src/filesystem/dfilewatcher.cpp @@ -0,0 +1,277 @@ +/** + * Copyright (C) 2017 Deepin Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + **/ + +#include "dfilewatcher.h" +#include "private/dbasefilewatcher_p.h" + +#include "dfilesystemwatcher.h" + +#include +#include + +DCORE_BEGIN_NAMESPACE + +static QString joinFilePath(const QString &path, const QString &name) +{ + if (path.endsWith(QDir::separator())) + return path + name; + + return path + QDir::separator() + name; +} + +class DFileWatcherPrivate : DBaseFileWatcherPrivate +{ +public: + DFileWatcherPrivate(DFileWatcher *qq) + : DBaseFileWatcherPrivate(qq) {} + + bool start() Q_DECL_OVERRIDE; + bool stop() Q_DECL_OVERRIDE; + + void _q_handleFileDeleted(const QString &path, const QString &parentPath); + void _q_handleFileAttributeChanged(const QString &path, const QString &parentPath); + void _q_handleFileMoved(const QString &from, const QString &fromParent, const QString &to, const QString &toParent); + void _q_handleFileCreated(const QString &path, const QString &parentPath); + void _q_handleFileModified(const QString &path, const QString &parentPath); + void _q_handleFileClose(const QString &path, const QString &parentPath); + + static QString formatPath(const QString &path); + + QString path; + QStringList watchFileList; + + static QMap filePathToWatcherCount; + + Q_DECLARE_PUBLIC(DFileWatcher) +}; + +QMap DFileWatcherPrivate::filePathToWatcherCount; +Q_GLOBAL_STATIC(DFileSystemWatcher, watcher_file_private) + +QStringList parentPathList(const QString &path) +{ + QStringList list; + QDir dir(path); + + list << path; + + while (dir.cdUp()) { + list << dir.absolutePath(); + } + + return list; +} + +bool DFileWatcherPrivate::start() +{ + Q_Q(DFileWatcher); + + started = true; + + Q_FOREACH (const QString &path, parentPathList(this->path)) { + if (watchFileList.contains(path)) + continue; + + if (filePathToWatcherCount.value(path, -1) <= 0) { + if (!watcher_file_private->addPath(path)) { + qWarning() << Q_FUNC_INFO << "start watch failed, file path =" << path; + q->stopWatcher(); + started = false; + return false; + } + } + + watchFileList << path; + filePathToWatcherCount[path] = filePathToWatcherCount.value(path, 0) + 1; + } + + q->connect(watcher_file_private, &DFileSystemWatcher::fileDeleted, + q, &DFileWatcher::onFileDeleted); + q->connect(watcher_file_private, &DFileSystemWatcher::fileAttributeChanged, + q, &DFileWatcher::onFileAttributeChanged); + q->connect(watcher_file_private, &DFileSystemWatcher::fileMoved, + q, &DFileWatcher::onFileMoved); + q->connect(watcher_file_private, &DFileSystemWatcher::fileCreated, + q, &DFileWatcher::onFileCreated); + q->connect(watcher_file_private, &DFileSystemWatcher::fileModified, + q, &DFileWatcher::onFileModified); + q->connect(watcher_file_private, &DFileSystemWatcher::fileClosed, + q, &DFileWatcher::onFileClosed); + + return true; +} + +bool DFileWatcherPrivate::stop() +{ + Q_Q(DFileWatcher); + + q->disconnect(watcher_file_private, 0, q, 0); + + bool ok = true; + + Q_FOREACH (const QString &path, watchFileList) { + int count = filePathToWatcherCount.value(path, 0); + + --count; + + if (count <= 0) { + filePathToWatcherCount.remove(path); + watchFileList.removeOne(path); + ok = ok && watcher_file_private->removePath(path); + } else { + filePathToWatcherCount[path] = count; + } + } + + return ok; +} + +void DFileWatcherPrivate::_q_handleFileDeleted(const QString &path, const QString &parentPath) +{ + if (path != this->path && parentPath != this->path) + return; + + Q_Q(DFileWatcher); + + Q_EMIT q->fileDeleted(QUrl::fromLocalFile(path)); +} + +void DFileWatcherPrivate::_q_handleFileAttributeChanged(const QString &path, const QString &parentPath) +{ + if (path != this->path && parentPath != this->path) + return; + + Q_Q(DFileWatcher); + + Q_EMIT q->fileAttributeChanged(QUrl::fromLocalFile(path)); +} + +void DFileWatcherPrivate::_q_handleFileMoved(const QString &from, const QString &fromParent, const QString &to, const QString &toParent) +{ + Q_Q(DFileWatcher); + + if ((fromParent == this->path && toParent == this->path) || from == this->path) { + Q_EMIT q->fileMoved(QUrl::fromLocalFile(from), QUrl::fromLocalFile(to)); + } else if (fromParent == this->path) { + Q_EMIT q->fileDeleted(QUrl::fromLocalFile(from)); + } else if (watchFileList.contains(from)) { + Q_EMIT q->fileDeleted(url); + } else if (toParent == this->path) { + Q_EMIT q->subfileCreated(QUrl::fromLocalFile(to)); + } +} + +void DFileWatcherPrivate::_q_handleFileCreated(const QString &path, const QString &parentPath) +{ + if (path != this->path && parentPath != this->path) + return; + + Q_Q(DFileWatcher); + + Q_EMIT q->subfileCreated(QUrl::fromLocalFile(path)); +} + +void DFileWatcherPrivate::_q_handleFileModified(const QString &path, const QString &parentPath) +{ + if (path != this->path && parentPath != this->path) + return; + + Q_Q(DFileWatcher); + + Q_EMIT q->fileModified(QUrl::fromLocalFile(path)); +} + +void DFileWatcherPrivate::_q_handleFileClose(const QString &path, const QString &parentPath) +{ + if (path != this->path && parentPath != this->path) + return; + + Q_Q(DFileWatcher); + + Q_EMIT q->fileClosed(QUrl::fromLocalFile(path)); +} + +QString DFileWatcherPrivate::formatPath(const QString &path) +{ + QString p = QFileInfo(path).absoluteFilePath(); + + if (p.endsWith(QDir::separator())) + p.chop(1); + + return p.isEmpty() ? path : p; +} + +DFileWatcher::DFileWatcher(const QString &filePath, QObject *parent) + : DBaseFileWatcher(*new DFileWatcherPrivate(this), QUrl::fromLocalFile(filePath), parent) +{ + d_func()->path = DFileWatcherPrivate::formatPath(filePath); +} + +void DFileWatcher::onFileDeleted(const QString &path, const QString &name) +{ + if (name.isEmpty()) + d_func()->_q_handleFileDeleted(path, QString()); + else + d_func()->_q_handleFileDeleted(joinFilePath(path, name), path); +} + +void DFileWatcher::onFileAttributeChanged(const QString &path, const QString &name) +{ + if (name.isEmpty()) + d_func()->_q_handleFileAttributeChanged(path, QString()); + else + d_func()->_q_handleFileAttributeChanged(joinFilePath(path, name), path); +} + +void DFileWatcher::onFileMoved(const QString &from, const QString &fname, const QString &to, const QString &tname) +{ + QString fromPath, fpPath; + QString toPath, tpPath; + + if (fname.isEmpty()) { + fromPath = from; + } else { + fromPath = joinFilePath(from, fname); + fpPath = from; + } + + if (tname.isEmpty()) { + toPath = to; + } else { + toPath = joinFilePath(to, tname); + tpPath = to; + } + + d_func()->_q_handleFileMoved(fromPath, fpPath, toPath, tpPath); +} + +void DFileWatcher::onFileCreated(const QString &path, const QString &name) +{ + d_func()->_q_handleFileCreated(joinFilePath(path, name), path); +} + +void DFileWatcher::onFileModified(const QString &path, const QString &name) +{ + if (name.isEmpty()) + d_func()->_q_handleFileModified(path, QString()); + else + d_func()->_q_handleFileModified(joinFilePath(path, name), path); +} + +void DFileWatcher::onFileClosed(const QString &path, const QString &name) +{ + if (name.isEmpty()) + d_func()->_q_handleFileClose(path, QString()); + else + d_func()->_q_handleFileClose(joinFilePath(path, name), path); +} + +DCORE_END_NAMESPACE + +#include "moc_dfilewatcher.cpp" diff --git a/src/filesystem/dfilewatcher.h b/src/filesystem/dfilewatcher.h new file mode 100644 index 0000000..7dba2ec --- /dev/null +++ b/src/filesystem/dfilewatcher.h @@ -0,0 +1,40 @@ +/** + * Copyright (C) 2017 Deepin Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + **/ + +#ifndef DFILEWATCHER_H +#define DFILEWATCHER_H + +#include "dbasefilewatcher.h" + +DCORE_BEGIN_NAMESPACE + +class DFileWatcherPrivate; +class DFileWatcher : public DBaseFileWatcher +{ + Q_OBJECT + +public: + explicit DFileWatcher(const QString &filePath, QObject *parent = 0); + +private Q_SLOTS: + void onFileDeleted(const QString &path, const QString &name); + void onFileAttributeChanged(const QString &path, const QString &name); + void onFileMoved(const QString &fromPath, const QString &fromName, + const QString &toPath, const QString &toName); + void onFileCreated(const QString &path, const QString &name); + void onFileModified(const QString &path, const QString &name); + void onFileClosed(const QString &path, const QString &name); + +private: + D_DECLARE_PRIVATE(DFileWatcher) +}; + +DCORE_END_NAMESPACE + +#endif // DFILEWATCHER_H diff --git a/src/filesystem/dfilewatchermanager.cpp b/src/filesystem/dfilewatchermanager.cpp new file mode 100644 index 0000000..e5b60cf --- /dev/null +++ b/src/filesystem/dfilewatchermanager.cpp @@ -0,0 +1,95 @@ +/** + * Copyright (C) 2017 Deepin Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + **/ + +#include "dfilewatchermanager.h" +#include "dfilewatcher.h" +#include "base/private/dobject_p.h" + +#include +#include + +DCORE_BEGIN_NAMESPACE + +class DFileWatcherManagerPrivate : public DObjectPrivate +{ +public: + DFileWatcherManagerPrivate(DFileWatcherManager *qq); + + QMap watchersMap; + + D_DECLARE_PUBLIC(DFileWatcherManager) +}; + +DFileWatcherManagerPrivate::DFileWatcherManagerPrivate(DFileWatcherManager *qq) + : DObjectPrivate(qq) +{ + +} + +DFileWatcherManager::DFileWatcherManager(QObject *parent) + : QObject(parent) + , DObject(*new DFileWatcherManagerPrivate(this)) +{ + +} + +DFileWatcherManager::~DFileWatcherManager() +{ + +} + +DFileWatcher *DFileWatcherManager::add(const QString &filePath) +{ + Q_D(DFileWatcherManager); + + DFileWatcher *watcher = d->watchersMap.value(filePath); + + if (watcher) { + return watcher; + } + + watcher = new DFileWatcher(filePath, this); + + connect(watcher, &DFileWatcher::fileAttributeChanged, this, [this](const QUrl & url) { + Q_EMIT fileAttributeChanged(url.toLocalFile()); + }); + connect(watcher, &DFileWatcher::fileClosed, this, [this](const QUrl & url) { + Q_EMIT fileClosed(url.toLocalFile()); + }); + connect(watcher, &DFileWatcher::fileDeleted, this, [this](const QUrl & url) { + Q_EMIT fileDeleted(url.toLocalFile()); + }); + connect(watcher, &DFileWatcher::fileModified, this, [this](const QUrl & url) { + Q_EMIT fileModified(url.toLocalFile()); + }); + connect(watcher, &DFileWatcher::fileMoved, this, [this](const QUrl & fromUrl, const QUrl & toUrl) { + Q_EMIT fileMoved(fromUrl.toLocalFile(), toUrl.toLocalFile()); + }); + connect(watcher, &DFileWatcher::subfileCreated, this, [this](const QUrl & url) { + Q_EMIT subfileCreated(url.toLocalFile()); + }); + + d->watchersMap[filePath] = watcher; + watcher->startWatcher(); + + return watcher; +} + +void DFileWatcherManager::remove(const QString &filePath) +{ + Q_D(DFileWatcherManager); + + DFileWatcher *watcher = d->watchersMap.take(filePath); + + if (watcher) { + watcher->deleteLater(); + } +} + +DCORE_END_NAMESPACE diff --git a/src/filesystem/dfilewatchermanager.h b/src/filesystem/dfilewatchermanager.h new file mode 100644 index 0000000..f6e3e55 --- /dev/null +++ b/src/filesystem/dfilewatchermanager.h @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2017 Deepin Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + **/ + +#ifndef DFILEWATCHERMANAGER_H +#define DFILEWATCHERMANAGER_H + +#include "dtkcore_global.h" +#include "base/dobject.h" + +#include + +DCORE_BEGIN_NAMESPACE + +class DFileWatcher; + +class DFileWatcherManagerPrivate; +class DFileWatcherManager : public QObject, public DObject +{ + Q_OBJECT + +public: + explicit DFileWatcherManager(QObject *parent = 0); + ~DFileWatcherManager(); + + DFileWatcher *add(const QString &filePath); + void remove(const QString &filePath); + +Q_SIGNALS: + void fileDeleted(const QString &filePath); + void fileAttributeChanged(const QString &filePath); + void fileMoved(const QString &fromFilePath, const QString &toFilePath); + void subfileCreated(const QString &filePath); + void fileModified(const QString &filePath); + void fileClosed(const QString &filePath); + +private: + QScopedPointer d_ptr; + + D_DECLARE_PRIVATE(DFileWatcherManager) + Q_DISABLE_COPY(DFileWatcherManager) +}; + +DCORE_END_NAMESPACE + +#endif // DFILEWATCHERMANAGER_H diff --git a/src/filesystem/dpathbuf.h b/src/filesystem/dpathbuf.h new file mode 100644 index 0000000..5179247 --- /dev/null +++ b/src/filesystem/dpathbuf.h @@ -0,0 +1,53 @@ +#pragma once + +#include + +#include "dtkcore_global.h" + +DCORE_BEGIN_NAMESPACE + +class DPathBuf +{ +public: + DPathBuf(const QString &path) + { + m_path = QDir(path).absolutePath(); + } + + DPathBuf operator/(const QString &p) const + { + return DPathBuf(m_path + "/" + p); + } + + DPathBuf &operator/=(const QString &p) + { + return join(p); + } + + DPathBuf operator/(const char *p) const + { + return operator /(QString(p)); + } + + DPathBuf &operator/=(const char *p) + { + return operator /=(QString(p)); + } + + DPathBuf &join(const QString &p) + { + m_path += "/" + p; + m_path = QDir(m_path).absolutePath(); + return *this; + } + + QString toString() const + { + return QDir::toNativeSeparators(m_path); + } + +private: + QString m_path; +}; + +DCORE_END_NAMESPACE diff --git a/src/filesystem/filesystem.pri b/src/filesystem/filesystem.pri new file mode 100644 index 0000000..01ff7b5 --- /dev/null +++ b/src/filesystem/filesystem.pri @@ -0,0 +1,37 @@ +include($$PWD/private/private.pri) + +HEADERS += \ + $$PWD/dbasefilewatcher.h \ + $$PWD/dfilesystemwatcher.h \ + $$PWD/dfilewatcher.h \ + $$PWD/dfilewatchermanager.h \ + $$PWD/dpathbuf.h + +SOURCES += \ + $$PWD/dbasefilewatcher.cpp \ + $$PWD/dfilewatcher.cpp \ + $$PWD/dfilewatchermanager.cpp + + +linux { + CONFIG += link_pkgconfig + PKGCONFIG += gsettings-qt + + SOURCES += \ + $$PWD/dfilesystemwatcher_linux.cpp +} else:win* { + SOURCES += \ + $$PWD/dfilesystemwatcher_win.cpp +} else:mac* { + SOURCES += \ + $$PWD/dfilesystemwatcher_win.cpp +} + +includes.files += $$PWD/*.h +includes.files += $$PWD/*.cpp +includes.files += \ + $$PWD/DFileWatcher \ + $$PWD/DBaseFileWatcher \ + $$PWD/DFileSystemWatcher \ + $$PWD/DFileWatcherManager \ + $$PWD/DPathBuf diff --git a/src/filesystem/private/dbasefilewatcher_p.h b/src/filesystem/private/dbasefilewatcher_p.h new file mode 100644 index 0000000..9304e15 --- /dev/null +++ b/src/filesystem/private/dbasefilewatcher_p.h @@ -0,0 +1,37 @@ +/** + * Copyright (C) 2017 Deepin Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + **/ + +#ifndef DBASEFILEWATCHER_P_H +#define DBASEFILEWATCHER_P_H + +#include "base/private/dobject_p.h" + +#include + +DCORE_BEGIN_NAMESPACE + +class DBaseFileWatcher; +class DBaseFileWatcherPrivate : public DObjectPrivate +{ +public: + DBaseFileWatcherPrivate(DBaseFileWatcher *qq); + + virtual bool start() = 0; + virtual bool stop() = 0; + + QUrl url; + bool started = false; + static QList watcherList; + + D_DECLARE_PUBLIC(DBaseFileWatcher) +}; + +DCORE_END_NAMESPACE + +#endif // DBASEFILEWATCHER_P_H diff --git a/src/filesystem/private/dfilesystemwatcher_linux_p.h b/src/filesystem/private/dfilesystemwatcher_linux_p.h new file mode 100644 index 0000000..ceb409b --- /dev/null +++ b/src/filesystem/private/dfilesystemwatcher_linux_p.h @@ -0,0 +1,50 @@ +/** + * Copyright (C) 2017 Deepin Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + **/ + +#ifndef DFILESYSTEMWATCHER_P_H +#define DFILESYSTEMWATCHER_P_H + +#include "base/private/dobject_p.h" + +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +class DFileSystemWatcher; +class DFileSystemWatcherPrivate : public DObjectPrivate +{ + Q_DECLARE_PUBLIC(DFileSystemWatcher) + +public: + DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq); + ~DFileSystemWatcherPrivate(); + + QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories); + QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories); + + QStringList files, directories; + int inotifyFd; + QHash pathToID; + QMultiHash idToPath; + QSocketNotifier notifier; + + // private slots + void _q_readFromInotify(); + +private: + QString getPathFromID(int id) const; + void onFileChanged(const QString &path, bool removed); + void onDirectoryChanged(const QString &path, bool removed); +}; + +DCORE_END_NAMESPACE + +#endif // DFILESYSTEMWATCHER_P_H diff --git a/src/filesystem/private/dfilesystemwatcher_win_p.h b/src/filesystem/private/dfilesystemwatcher_win_p.h new file mode 100644 index 0000000..52d8af5 --- /dev/null +++ b/src/filesystem/private/dfilesystemwatcher_win_p.h @@ -0,0 +1,36 @@ +/** + * Copyright (C) 2017 Deepin Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + **/ +#ifndef DFILESYSTEMWATCHER_WIN_P_H +#define DFILESYSTEMWATCHER_WIN_P_H + +#include "base/private/dobject_p.h" + +DCORE_BEGIN_NAMESPACE + +class DFileSystemWatcher; +class DFileSystemWatcherPrivate : public DObjectPrivate +{ + Q_DECLARE_PUBLIC(DFileSystemWatcher) + +public: + DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq); + ~DFileSystemWatcherPrivate(); + + // private slots + void _q_readFromInotify(); +}; + +void DFileSystemWatcherPrivate::_q_readFromInotify() +{ + +} + +DCORE_END_NAMESPACE + +#endif // DFILESYSTEMWATCHER_WIN_P_H diff --git a/src/filesystem/private/private.pri b/src/filesystem/private/private.pri new file mode 100644 index 0000000..b31875f --- /dev/null +++ b/src/filesystem/private/private.pri @@ -0,0 +1,10 @@ +HEADERS += \ + $$PWD/dbasefilewatcher_p.h + +linux { + HEADERS += \ + $$PWD/dfilesystemwatcher_linux_p.h +} else:win* { + HEADERS += \ + $$PWD/dfilesystemwatcher_win_p.h +} diff --git a/src/install.pri b/src/install.pri index 17ff73b..f17f599 100644 --- a/src/install.pri +++ b/src/install.pri @@ -1,18 +1,7 @@ includes.path = $${DTK_INCLUDEPATH}/DCore -includes.files += $$PWD/base/*.h -includes.files += $$PWD/base/*.cpp -includes.files += \ - $$PWD/base/DObject \ - $$PWD/base/DSingleton - -includes.files += $$PWD/log/*.h -includes.files += $$PWD/log/*.cpp -includes.files += \ - $$PWD/log/DLog INSTALLS += includes target - QMAKE_PKGCONFIG_LIBDIR = $$target.path QMAKE_PKGCONFIG_VERSION = $$VERSION QMAKE_PKGCONFIG_DESTDIR = pkgconfig diff --git a/src/log/log.pri b/src/log/log.pri index 50b1897..b263dfc 100644 --- a/src/log/log.pri +++ b/src/log/log.pri @@ -1 +1,6 @@ include(cutelogger.pri) + +includes.files += $$PWD/*.h +includes.files += $$PWD/*.cpp +includes.files += \ + $$PWD/DLog diff --git a/src/src.pro b/src/src.pro index 6442560..9aa7dd1 100644 --- a/src/src.pro +++ b/src/src.pro @@ -1,7 +1,7 @@ include($$PWD/lib.pri) -include($$PWD/install.pri) QT -= gui +QT += dbus TARGET = dtkcore @@ -13,5 +13,9 @@ INCLUDEPATH += $$PWD HEADERS += \ $$PWD/dtkcore_global.h \ -include($$PWD/base/dbase.pri) +include($$PWD/base/base.pri) include($$PWD/log/log.pri) +include($$PWD/filesystem/filesystem.pri) + +include($$PWD/install.pri) + -- 2.30.2